﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

namespace Fine.CRM.Common
{
    public static class QueryTraceExtensions
    {
        /// <summary>
        /// Parses a query to provide a text representation to assist in troubleshooting and testing.
        /// </summary>
        /// <param name="qry"></param>
        /// <returns></returns>
        public static string ParseQueryExpression(this QueryExpression qry)
        {
            int level = 1;

            string rv = new string(' ', 1) + "\r\n";
            rv += string.Format("Entity = {0}\r\n", qry.EntityName);

            if (qry.ColumnSet.AllColumns)
            {
                rv += string.Format("Columns = All Columns\r\n");
            }
            else
            {
                rv += string.Format("Columns = {0}\r\n", string.Join(", ", qry.ColumnSet.Columns.ToArray()));
            }

            rv += string.Format("Distinct = {0}\r\n", qry.Distinct);

            rv += string.Format("NoLock = {0}\r\n", qry.NoLock);

            if (qry.PageInfo != null)
            {
                rv += "Page Info Available\r\n";
            }
            else
            {
                rv += "Page Info Not Available\r\n";
            }

            //try
            //{
            //    if (!qry.TopCount.HasValue)
            //    {
            //        rv += "Top Count Not Availalbe\r\n";
            //    }
            //    else
            //    {
            //        rv += string.Format("Top Count = {0}\r\n", qry.TopCount.Value);
            //    }
            //}
            //catch (Exception ex) { }
            //finally { }

            if (qry.Criteria != null)
            {
                rv += parseCriteria(qry.Criteria, level);
            }

            foreach (LinkEntity link in qry.LinkEntities)
            {
                rv += parseLinkEntity(link, level);
            }

            foreach (OrderExpression order in qry.Orders)
            {
                rv += string.Format("Order: {0} - {1}\r\n", order.AttributeName, order.OrderType.ToString());
            }

            rv += new string('-', 40);

            return rv;
        }

        private static string parseCriteria(FilterExpression Criteria, int ParentLevel)
        {
            if (Criteria == null)
            {
                return string.Empty;
            }

            string spacer = new string(' ', (2 * ParentLevel));
            string rv = string.Format("{0}Filter ", spacer);

            if (Criteria.FilterOperator == LogicalOperator.Or)
            {
                rv += "(FilterOperator = Or)\r\n";
            }
            else
            {
                rv += "(FilterOperator = And)\r\n";
            }

            rv += parseConditions(Criteria, ParentLevel + 1);

            foreach (FilterExpression item in Criteria.Filters)
            {
                rv += parseCriteria(item, ParentLevel + 1);
            }

            return rv;
        }

        private static string parseConditions(FilterExpression Criteria, int ParentLevel)
        {

            string spacer = new string(' ', (2 * ParentLevel));
            string rv = string.Empty;

            foreach (ConditionExpression condition in Criteria.Conditions)
            {
                List<string> values = new List<string>();
                foreach (object value in condition.Values)
                {
                    values.Add(value.ToString());
                }

                rv += string.Format("{0}Condition: {1} {2} {3}\r\n", spacer, condition.AttributeName, condition.Operator.ToString(), string.Join(", ", values.ToArray()));
            }

            return rv;
        }

        private static string parseLinkEntity(LinkEntity Link, int ParentLevel)
        {
            if (Link == null)
            {
                return string.Empty;
            }

            string spacer = new string(' ', (2 * ParentLevel));
            string rv = string.Format("{0}LinkedEntity (Join Operator: {1})\r\n", spacer, Link.JoinOperator.ToString());

            spacer = new string(' ', (2 * (ParentLevel + 1)));
            try
            {
                if (Link.EntityAlias != null)
                {
                    rv += string.Format("{0}EntityAlias = {1}\r\n", spacer, Link.EntityAlias);
                }
            }
            catch
            {
            }

            rv += string.Format("{0}LinkFromEntity = {1}\r\n", spacer, Link.LinkFromEntityName);
            rv += string.Format("{0}LinkFromAttribute = {1}\r\n", spacer, Link.LinkFromAttributeName);
            rv += string.Format("{0}LinkToEntity = {1}\r\n", spacer, Link.LinkToEntityName);
            rv += string.Format("{0}LinkToAttribute = {1}\r\n", spacer, Link.LinkToAttributeName);

            if (Link.Columns.AllColumns)
            {
                rv += string.Format("{0}Columns = All Columns\r\n", spacer);
            }
            else
            {
                rv += string.Format("{0}Columns = {1}\r\n", spacer, string.Join(", ", Link.Columns.Columns.ToArray()));
            }


            rv += parseCriteria(Link.LinkCriteria, ParentLevel + 1);

            foreach (LinkEntity child in Link.LinkEntities)
            {
                rv += parseLinkEntity(child, ParentLevel + 1);
            }

            return rv;
        }
    }
}
